/**
 * Copyright 2019 吉鼎科技.

 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.easyplatform.web.controller.admin;

import cn.easyplatform.compiler.Compiler;
import cn.easyplatform.compiler.impl.JdkCompiler;
import cn.easyplatform.lang.Strings;
import cn.easyplatform.messages.request.SimpleRequestMessage;
import cn.easyplatform.messages.request.SimpleTextRequestMessage;
import cn.easyplatform.messages.vos.addon.SearchVo;
import cn.easyplatform.messages.vos.admin.CustomVo;
import cn.easyplatform.messages.vos.admin.EntityVo;
import cn.easyplatform.spi.extension.ApplicationService;
import cn.easyplatform.spi.service.AddonService;
import cn.easyplatform.spi.service.AdminService;
import cn.easyplatform.type.EntityType;
import cn.easyplatform.type.IResponseMessage;
import cn.easyplatform.type.SubType;
import cn.easyplatform.web.dialog.MessageBox;
import cn.easyplatform.web.ext.cmez.CMeditor;
import cn.easyplatform.web.service.ServiceLocator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zkoss.util.resource.Labels;
import org.zkoss.zk.ui.Page;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.util.Clients;
import org.zkoss.zul.*;

/**
 * @author <a href="mailto:davidchen@epclouds.com">littleDog</a> <br/>
 * @since 2.0.0 <br/>
 */
public class ClassDialog extends Window implements EventListener<Event> {

    /**
     *
     */
    private static final long serialVersionUID = 1L;

    private final static Logger LOG = LoggerFactory.getLogger(ClassDialog.class);

    private CustomVo cb;

    private Listbox listbox;

    private CMeditor editor;

    private Textbox txName;
    private Bandbox txClass;
    private Textbox txDescription;

    private CustomVo.Entry selelected;

    public ClassDialog(CustomVo cb) {
        this.cb = cb;
        selelected = new CustomVo.Entry("", "");
        selelected.setFlag('C');
        editor = new CMeditor();
        listbox = new Listbox();
        Listhead head = new Listhead();
        head.setSizable(true);
        Listheader header;
        if (cb.getType() == 1) {
            header = new Listheader(Labels.getLabel("admin.project.name"));
            header.setHflex("2");
            head.appendChild(header);
        }
        header = new Listheader(Labels.getLabel("admin.project.class"));
        header.setHflex("2");
        head.appendChild(header);
        header = new Listheader(Labels.getLabel("admin.project.desp"));
        header.setHflex("3");
        head.appendChild(header);
        head.setParent(listbox);
        setWidth("80%");
        setHeight("80%");
        setTitle(Labels.getLabel("admin." + (cb.getType() == 1 ? "commands" : "services")));
    }

    public void showDialog(Page page) {
        setClosable(true);
        setMaximizable(true);
        setBorder("normal");

        Borderlayout layout = new Borderlayout();
        Center container = new Center();
        Vlayout vlayout = new Vlayout();
        vlayout.setHflex("1");
        vlayout.setVflex("1");
        Hlayout hlayout = new Hlayout();
        hlayout.setSclass("m-1");
        hlayout.setHflex("1");
        if (cb.getType() == 1) {
            hlayout.appendChild(new Label(Labels.getLabel("admin.project.name") + ":"));
            txName = new Textbox();
            txName.setWidth("80px");
            txName.setParent(hlayout);
        }
        hlayout.appendChild(new Label(Labels.getLabel("admin.project.class") + ":"));
        txClass = new Bandbox();
        txClass.addEventListener(Events.ON_OPEN, this);
        txClass.setParent(hlayout);

        hlayout.appendChild(new Label(Labels.getLabel("admin.project.desp") + ":"));
        txDescription = new Textbox();
        //txDescription.setWidth("300px");
        txDescription.setParent(hlayout);

        Button add = new Button(Labels.getLabel("button.create"));
        add.setId("add");
        add.setIconSclass("z-icon-plus");
        add.setSclass("mr-1");
        add.addEventListener(Events.ON_CLICK, this);
        add.setParent(hlayout);
        Button delete = new Button(Labels.getLabel("button.delete"));
        delete.setId("delete");
        delete.setIconSclass("z-icon-times");
        delete.setSclass("mr-1");
        delete.addEventListener(Events.ON_CLICK, this);
        delete.setParent(hlayout);

        Button save = new Button(Labels.getLabel("button.save"));
        save.setId("save");
        save.setIconSclass("z-icon-save");
        save.setParent(hlayout);
        save.addEventListener(Events.ON_CLICK, this);

        Button format = new Button(Labels.getLabel("button.beautify"));
        format.setIconSclass("z-icon-magic");
        format.setSclass("ml-2");
        format.addEventListener(Events.ON_CLICK, this);
        format.setId("format");
        format.setParent(hlayout);

        hlayout.setParent(vlayout);

        editor.setMode("text/x-java");
        editor.setHflex("1");
        editor.setVflex("1");
        editor.setLineNumbers(true);
        editor.setParent(vlayout);

        vlayout.setParent(container);
        container.setParent(layout);

        West west = new West();
        west.setSize("30%");
        west.setBorder("none");
        west.setSplittable(true);
        createItems();
        listbox.setHflex("1");
        listbox.setVflex("1");
        listbox.addEventListener(Events.ON_SELECT, this);
        listbox.setParent(west);
        west.setParent(layout);

        layout.setParent(this);
        setPage(page);
        setSizable(true);
        setPosition("center,top");
        doHighlighted();
    }

    private void createItems() {
        if (cb.getEntries() != null) {
            for (CustomVo.Entry entry : cb.getEntries())
                createItem(entry);
        }
    }

    private void createItem(CustomVo.Entry entry) {
        Listitem li = new Listitem();
        if (cb.getType() == 1)
            li.appendChild(new Listcell(entry.getName()));
        li.appendChild(new Listcell(entry.getClassName()));
        li.appendChild(new Listcell(entry.getDescription()));
        li.setValue(entry);
        li.setParent(listbox);
    }

    @Override
    public void onEvent(Event event) throws Exception {
        if (event.getName().equals(Events.ON_SELECT)) {
            selelected = listbox.getSelectedItem().getValue();
            if (cb.getType() == 1) {
                txName.setValue(selelected.getName());
                txName.setReadonly(true);
            } else
                txClass.setDisabled(true);
            txClass.setValue(selelected.getClassName());
            txDescription.setValue(selelected.getDescription());
            if (!selelected.getClassName().contains(".")) {
                IResponseMessage<?> resp = ServiceLocator.lookup(AddonService.class).getEntity(new SimpleTextRequestMessage(selelected.getClassName()));
                if (resp.isSuccess()) {
                    if (resp.getBody() != null) {
                        EntityVo ev = (EntityVo) resp.getBody();
                        editor.setValue(ev.getContent());
                    }
                } else
                    MessageBox.showMessage(resp);
            }
        } else if (event.getTarget() == txClass) {
            SearchVo vo = new SearchVo();
            vo.setType(EntityType.RESOURCE.getName());
            vo.setSubType(SubType.CLASS.getName());
            new EntityDialog(value -> {
                txClass.setValue(value[0]);
                txDescription.setValue(value[1]);
                IResponseMessage<?> resp = ServiceLocator.lookup(AddonService.class).getEntity(new SimpleTextRequestMessage(value[0]));
                if (resp.isSuccess()) {
                    EntityVo ev = (EntityVo) resp.getBody();
                    editor.setValue(ev.getContent());
                }
            }).showDialog(txClass.getPage(), vo);
        } else {
            String id = event.getTarget().getId();
            if ("save".equals(id)) {
                save((Button) event.getTarget());
            } else if ("add".equals(id)) {
                createEntry();
            } else if ("delete".equals(id)) {
                if (listbox.getSelectedItem() != null) {
                    CustomVo.Entry entry = listbox.getSelectedItem().getValue();
                    Messagebox.show(Labels.getLabel("admin.service.op.confirm", new String[]{entry.getClassName()}), Labels.getLabel("admin.service.op.title"), Messagebox.NO | Messagebox.OK,
                            Messagebox.QUESTION, event1 -> {
                                if (event1.getName().equals(Events.ON_OK)) {
                                    delete();
                                }
                            });
                }
            } else if ("format".equals(id)) {
                if (!Strings.isBlank((String) editor.getValue()))
                    editor.formatAll();
            }
        }
    }

    private void save(Button btn) {
        if (cb.getType() == 1 && Strings.isBlank(txName.getValue())) {
            Clients.wrongValue(txName, Labels.getLabel("common.input.not.empty", new Object[]{Labels.getLabel("admin.project.name")}));
            return;
        }
        if (Strings.isBlank(txClass.getValue())) {
            Clients.wrongValue(txClass, Labels.getLabel("common.input.not.empty", new Object[]{Labels.getLabel("admin.project.class")}));
            return;
        }
        if (Strings.isBlank(txDescription.getValue())) {
            Clients.wrongValue(txDescription, Labels.getLabel("common.input.not.empty", new Object[]{Labels.getLabel("admin.project.desp")}));
            return;
        }
        //目标是参数类型
        if (!txClass.getValue().contains(".")) {
            if (Strings.isBlank((String) editor.getValue())) {
                Clients.wrongValue(btn, Labels.getLabel("common.input.not.empty", new Object[]{Labels.getLabel("msn.content")}));
                return;
            }
            String content = editor.getValue().toString();
            try {
                Compiler compiler = new JdkCompiler();
                Class<?> clazz = compiler.compile(content, Thread.currentThread().getContextClassLoader());
                //Interpreter interpreter = new Interpreter();
                //Class<?> clazz = (Class<?>) interpreter.eval(content);
                if (cb.getType() == 2) {//自定义服务
                    if (!ApplicationService.class.isAssignableFrom(clazz)) {
                        Clients.wrongValue(btn, Labels.getLabel("admin.project.custom.err0"));
                        return;
                    }
                }
            } catch (Throwable e) {
                if (LOG.isErrorEnabled())
                    LOG.error("compile class:", e);
                Clients.wrongValue(btn, Labels.getLabel("admin.project.custom.err1", new String[]{e.getMessage()}));
                return;
            }
            selelected.setContent(content);
        } else
            selelected.setContent(null);
        if (cb.getType() == 1)
            selelected.setName(txName.getValue().trim());
        selelected.setClassName(txClass.getValue().trim());
        if (selelected.getFlag() == 'C') {
            if (cb.getType() == 1 && (selelected.getName().equals("app") || selelected.getName().equals("dt") || selelected.getName().equals("util") || selelected.getName().equals("bpm") || selelected.getName().equals("db"))) {
                Clients.wrongValue(btn, Labels.getLabel("admin.project.custom.sys.exists", new String[]{selelected.getName()}));
                return;
            }
            if (cb.getEntries() != null) {
                for (CustomVo.Entry e : cb.getEntries()) {
                    if (cb.getType() == 1) {
                        if (selelected.getName().equals(e.getName())) {
                            Clients.wrongValue(btn, Labels.getLabel("admin.project.cust.exists", new String[]{e.getName()}));
                            return;
                        }
                    } else {
                        if (selelected.getClassName().equals(e.getClassName())) {
                            Clients.wrongValue(btn, Labels.getLabel("admin.project.cust.exists", new String[]{e.getClassName()}));
                            return;
                        }
                    }
                }
            }
        }
        selelected.setDescription(txDescription.getValue().trim());
        if (selelected.getFlag() == 'R')
            selelected.setFlag('U');
        AdminService as = ServiceLocator.lookup(AdminService.class);
        IResponseMessage<?> resp = as
                .saveProject(new SimpleRequestMessage(selelected));
        if (!resp.isSuccess())
            MessageBox.showMessage(resp);
        else {
            if (selelected.getFlag() == 'C') {
                createItem(selelected);
                selelected.setFlag('R');
                if (cb.getType() == 1)
                    txName.setReadonly(true);
                else
                    txClass.setDisabled(true);
            }
            MessageBox.showInfo(Labels.getLabel("admin.service.op.title"), Labels.getLabel("common.success", new Object[]{btn.getLabel()}));
        }
    }

    private void delete() {
        CustomVo.Entry entry = listbox.getSelectedItem().getValue();
        entry.setFlag('D');
        AdminService as = ServiceLocator.lookup(AdminService.class);
        IResponseMessage<?> resp = as
                .saveProject(new SimpleRequestMessage(entry));
        if (!resp.isSuccess())
            MessageBox.showMessage(resp);
        else {
            listbox.getSelectedItem().detach();
            createEntry();
        }
    }

    private void createEntry() {
        txClass.setValue("");
        txDescription.setValue("");
        if (cb.getType() == 1) {
            txName.setValue("");
            txName.setReadonly(false);
        } else
            txClass.setDisabled(false);
        editor.setValue("");
        selelected = new CustomVo.Entry("", "");
        selelected.setFlag('C');
    }
}
